﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using Nizm0.QOT.Library.QOTStorageWCFService;

namespace Nizm0.QOT.LocalStorage
{
    public enum StorageResponse { Exists, NotExists, NewWersion, Error, Off }

    [Serializable]
    [XmlRoot("LocalStorage")]
    [XmlInclude(typeof(Library.Objects.ProcedureHistory))]
    public class LocalStorage : System.Xml.Serialization.IXmlSerializable
    {

        //[XmlIgnore()]
        //private LocalStorageSettings _storageSettings;

        [XmlArray("ProcedureList"), XmlArrayItem("Procedure", typeof(Library.Objects.IProcedure))]
        public List<Library.Objects.ProcedureInfoHistory> _procedureList = new List<Library.Objects.ProcedureInfoHistory>();
        [XmlIgnore()]
        private object _lockobj = new object();
        [XmlIgnore()]
        private string _storageLocation;
        [XmlIgnore()]
        private string _fileName = "StorageList.xml";

        [XmlArray("ProjectList"), XmlArrayItem(typeof(Library.Objects.ExternalProject))]
        private List<Library.Objects.ExternalProject> _projectList = new List<Library.Objects.ExternalProject>();

        private bool _initialized = false;

        public LocalStorage()
            : this(true)
        { }

        public LocalStorage(bool loadfile)
        {
            if (loadfile)
            {
                _storageLocation = string.Format("{0}\\Storage\\", Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.Location);
                if (Directory.Exists(_storageLocation))
                {
                    Load();
                }
                else
                    Directory.CreateDirectory(_storageLocation);
            }
        }

        public void Load()
        {
            Load(_storageLocation + _fileName);
        }

        private void Load(string file)
        {
            if (File.Exists(file))
            {
                using (StreamReader strr = new StreamReader(file))
                {
                    try
                    {
                        //List<object> l = new List<object>();

                        //ReadXml(XmlReader.Create(_storageLocation + _fileName));
                        ReadXml(XmlReader.Create(strr));
                        //_listofprocedures.AddRange(Deserialize<List<Objects.Procedure>>(strr.ReadToEnd()));
                        _initialized = true;
                    }
                    catch
                    {
                    }
                    finally
                    {
                        strr.Close();
                    }
                    //_listofprocedures.AddRange();
                }
            }
        }

        public bool AddProcedure(Library.Objects.IProcedureInfo procedure)
        {
            if (!Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.Use) return false;// StorageResponse.Off;
            lock (_lockobj)
            {
                try
                {
                    if (_procedureList.Exists(p => p.Name == procedure.Name))
                    {
#if DEBUG
                        //ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
                        QOTStorageWCFServiceClient proxy = new QOTStorageWCFServiceClient();//"BasicHttpBinding_IProcedure", new EndpointAddress("http://http://localhost/QOT/")
                        //proxy.Endpoint.Address = new EndpointAddress(new Uri("http://localhost/QOT/QOTStorageWCFService.svc"), proxy.Endpoint.Address.Identity, proxy.Endpoint.Address.Headers);
                        proxy.Endpoint.Address = new EndpointAddress(new Uri(Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.ServerAdress), proxy.Endpoint.Address.Identity, proxy.Endpoint.Address.Headers);

                        proxy.Open();
                        string xml = ((Nizm0.QOT.Library.Objects.ProcedureInfo)procedure).Serialize();

                        proxy.AddProcedure(xml);
#endif
                        return true;
                    }
                    else
                    {
                            AddProcedure(new Library.Objects.ProcedureInfoHistory(procedure));

                            if (!string.IsNullOrWhiteSpace(Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.ServerAdress))
                            {
                                QOTStorageWCFServiceClient proxy = new QOTStorageWCFServiceClient();//"BasicHttpBinding_IProcedure", new EndpointAddress("http://http://localhost/QOT/")
                                //proxy.Endpoint.Address = new EndpointAddress(new Uri("http://localhost/QOT/QOTStorageWCFService.svc"), proxy.Endpoint.Address.Identity, proxy.Endpoint.Address.Headers);
                                proxy.Endpoint.Address = new EndpointAddress(new Uri(Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.ServerAdress), proxy.Endpoint.Address.Identity, proxy.Endpoint.Address.Headers);
                                proxy.Open();
                                string xml = ((Nizm0.QOT.Library.Objects.ProcedureInfo)procedure).Serialize();

                                proxy.AddProcedure(xml);
                            }

                            Save();

                            //using (StreamWriter strwr = new StreamWriter(_storageLocation + _fileName, false))
                            //{
                            //    try
                            //    {
                            //        string s = this.Serialize(strwr);

                            //        strwr.Close();
                            //    }
                            //    catch
                            //    {
                            //        strwr.Close();
                            //    }
                            //}
                        }
                        //AddProcedure(new Library.Objects.ProcedureHistory(procedure));
                        return true;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        private void AddProcedure(Library.Objects.ProcedureInfoHistory procedure)
        {
            _procedureList.Add(procedure);
        }

        public bool AddProcedure(Library.Objects.IProcedure procedure)
        {
            if (!Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.Use) return false;// StorageResponse.Off;
            lock (_lockobj)
            {
                if (_procedureList.Exists(p => p.Name == procedure.Name))
                {
                    Library.Objects.ProcedureInfoHistory tmpProc = (Library.Objects.ProcedureInfoHistory)_procedureList.Find(p => p.Name == procedure.Name);

                    if (tmpProc.ProcedureHistory == null)
                    {
                        tmpProc.ProcedureHistory = new Library.Objects.ProcedureHistory(procedure, _storageLocation);
                    }

                    if (AddProcedureVersion(tmpProc.ProcedureHistory, procedure) == StorageResponse.NewWersion)
                        return true;
                    else
                        return false;
                }
                return false;
            }
        }

        public bool VersionChange(object sender, Library.VersionChangeEventArgs e)
        {
            if (!Nizm0.QOT.LocalStorage.LocalStorageSettings.Instance.Use) return false;// StorageResponse.Off;
            lock (_lockobj)
            {
                if (_procedureList.Exists(p => p.Name == e.Old.Parent.Name))
                {
                    var tmpProc = _procedureList.Find(p => p.Name == e.Old.Parent.Name);
                    tmpProc.ProcedureHistory.ChangeVersion(e.New);
                    AddNewProject(e.New.Project);
                }
            }
            return true;
        }

        public bool AddQuery(Library.Objects.IProcedureInfo procedure, Library.Objects.Query query)
        {
            if (_procedureList.Exists(p => p.Name == procedure.Name))
            {
                Library.Objects.ProcedureInfoHistory tmpproc = _procedureList.Find(p => p.Name == procedure.Name);
                if (tmpproc.AddQuery(query) != null)
                    return true;
            }
            return false;
        }

        private StorageResponse AddProcedureVersion(Library.Objects.ProcedureHistory localprocedure, Library.Objects.IProcedure procedure)
        {
            StorageResponse locResponse;
            locResponse = localprocedure.AddNewVersion(procedure);
            //if (locResponse == StorageResponse.NewWersion)
            //{
            //}
            AddNewProject(procedure.Version.Project);
            return locResponse;
            //return StorageResponse.Exists;

            //if (localprocedure.ProcVersionList.Exists(v => v.Version == procedure.Version.Version))
            //{
            //    Library.Objects.ProcedureVersion tmpVersion = localprocedure.ProcVersionList.Find(v => v.Version == procedure.Version.Version);
            //    if (tmpVersion.Parent.Hash != procedure.Hash)
            //    {
            //        Library.Objects.ProcedureHistory tmpProcedure = (Library.Objects.ProcedureHistory)tmpVersion.Parent;
            //        UpdateProcedureVersion(tmpProcedure, procedure);
            //    }
            //}
            //else
            //{
            //    AddProcedureVersion(procedure.Version);
            //    tmpProc.KnownVersionList.Add(procedure.Version);
            //}
        }

        internal void SaveFile(Library.Objects.IProcedure procedure, Stream stream)
        {
            if (_procedureList.Exists(p => p.Name == procedure.Name))
            {
                Library.Objects.ProcedureHistory tmpProc = _procedureList.Find(p => p.Name == procedure.Name).ProcedureHistory;

                //var strw;
                try
                {
                    using (var strw = File.Create(tmpProc.Folder + procedure.Hash + ".sql"))
                    {
                        stream.CopyTo(strw);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        //private StorageResponse UpdateProcedureVersion(Library.Objects.ProcedureHistory localprocedure, Library.Objects.IProcedure procedure)
        //{
        //    StorageResponse result = StorageResponse.NotExists;
        //    string locationForProcedure = string.Format("{0}\\{1}\\", _storageLocation, procedure.Name);
        //    if (!Directory.Exists(locationForProcedure))
        //        Directory.CreateDirectory(locationForProcedure);

        //    result = localprocedure.AddNewVersion(procedure, locationForProcedure);
        //    AddNewProject(procedure.Version.Project);

        //    MemoryStream memstr = null;

        //    StreamWriter strw = null;
        //    try
        //    {
        //        memstr = new MemoryStream(Encoding.ASCII.GetBytes(procedure.Serialize()));

        //        strw = new StreamWriter(string.Format("{0}{1}.sql",locationForProcedure, procedure.Hash), false);
        //        strw.Write(procedure.Text);
        //    }
        //    catch (Exception ex)
        //    {
        //        return StorageResponse.Error;
        //    }
        //    finally
        //    {
        //        if (strw != null)
        //            strw.Close();
        //    }
        //    return result;
        //}

        private StorageResponse AddNewProject(string proj)
        {
            if (!string.IsNullOrWhiteSpace(proj))
            {
                Library.Objects.ExternalProject expr = new Library.Objects.ExternalProject(proj);
                AddNewProject(expr);
                //_projectList.Add(expr);

                Save();
                return StorageResponse.NotExists;
            }
            return StorageResponse.Exists;
        }

        private StorageResponse AddNewProject(Library.Objects.ExternalProject proj)
        {
            if (proj != null && !_projectList.Exists(p => p.Project == proj.Project))
            {
                _projectList.Add(proj);

                Save();
                return StorageResponse.NotExists;
            }
            return StorageResponse.Exists;
        }

        private void Save()
        {
            using (StreamWriter strwr = new StreamWriter(_storageLocation + _fileName, false))
            {
                try
                {
                    string s = this.Serialize(strwr);

                    strwr.Close();
                }
                catch
                {
                    strwr.Close();
                }
            }
        }

        private List<Library.Objects.ProcedureVersion> ReadVersions(Library.Objects.IProcedure proc)
        {
            return proc.ProcVersionList;
        }

        public string Serialize(StreamWriter str)
        {
            var settings = new XmlWriterSettings
            {
                Indent = true
            };

            var xml = new StringBuilder();
            using (var writer = XmlWriter.Create(str, settings))
            {
                //writer.WriteDocType("LocalStorage", null, "https://url", null);
                writer.WriteStartElement("LocalStorage");
                WriteXml(writer);
                writer.WriteEndElement(); // </APIRequest>
                return xml.ToString();
                //return ToXml(_listofprocedures);
            }
        }

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.MoveToContent();

            XmlDocument doc = new XmlDocument();
            doc.Load(reader);
            XmlNode node = doc.ChildNodes[0];
            //"LocalStorage/ProcedureList"
            foreach (XmlNode tmpnode in node.SelectNodes("//ProcedureInfoHistory"))
            {
                String className = tmpnode.Name.Trim(); //read the class name 
                //use the namespace IDelivery is located in
                className = "Nizm0.QOT.Library.Objects." + className;

                Type classType = Type.GetType(className);
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(Type.GetType(className));
                XmlReader r = new XmlNodeReader(tmpnode);
                _procedureList.Add((Library.Objects.ProcedureInfoHistory)Convert.ChangeType(serializer.Deserialize(r), classType));
            }
            foreach (XmlNode tmpnode in node.SelectNodes("//ExternalProject"))
            {
                String className = tmpnode.Name.Trim(); //read the class name 
                //use the namespace IDelivery is located in
                className = "Nizm0.QOT.Library.Objects." + className;

                Type classType = Type.GetType(className);
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(Type.GetType(className));
                XmlReader r = new XmlNodeReader(tmpnode);
                _projectList.Add((Library.Objects.ExternalProject)Convert.ChangeType(serializer.Deserialize(r), classType));
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {//ProcedureList
            writer.WriteStartElement("ProcedureList");
            WriteXmlProcedureList(writer);
            writer.WriteEndElement();
            writer.WriteStartElement("ProjectList");
            WriteXmlProjectList(writer);
            writer.WriteEndElement();
        }

        public void WriteXmlProcedureList(System.Xml.XmlWriter writer)
        {
            foreach (Library.Objects.IProcedureInfo proc in _procedureList)
            {
                var nsSerializer = new XmlSerializerNamespaces();
                nsSerializer.Add("", "");

                //var xmlSerializer = new XmlSerializer(typeof(Library.Objects.Procedure), "");
                System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(proc.GetType(), "");
                xmlSerializer.Serialize(writer, proc, nsSerializer);

                //writer.WriteElementString("Procedure", xmlSerializer.);
                //writer.WriteAttributeString("Version", this.Version);
            }
        }

        private void WriteXmlProjectList(System.Xml.XmlWriter writer)
        {
            foreach (Library.Objects.ExternalProject project in _projectList)
            {
                var nsSerializer = new XmlSerializerNamespaces();
                nsSerializer.Add("", "");

                //var xmlSerializer = new XmlSerializer(typeof(Library.Objects.Procedure), "");
                System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(project.GetType(), "");
                xmlSerializer.Serialize(writer, project, nsSerializer);

                //writer.WriteElementString("Procedure", xmlSerializer.);
                //writer.WriteAttributeString("Version", this.Version);
            }
        }

        #endregion
    }
}
